#Copyright Okhtay Shoghi 2008

require 'fileutils'
require 'find'
require 'test/unit'

require 'test/unit/ui/testrunnermediator'
require 'uranium/loggerwrapper'
require 'uranium/dbwrapper'
require 'uranium/callbacks'

Test::Unit.run=(true) #disable test/unit's auto run 

class TestObserver
	def initialize(suite_name, run_id, append_result=false)
		@run_id		 = run_id
		@suite_name = suite_name
		@append		 = append_result
	end
	
	def run(suite)
		mediator=Test::Unit::UI::TestRunnerMediator.new(suite)
		attach_listeners(mediator)
		mediator.run_suite
	end
	
	def attach_listeners(mediator)
		mediator.add_listener(Test::Unit::TestSuite::STARTED , &self.method(:suite_started))
		mediator.add_listener(Test::Unit::TestSuite::FINISHED, &self.method(:suite_finished))

		mediator.add_listener(Test::Unit::TestCase::STARTED	, &self.method(:test_started))
		mediator.add_listener(Test::Unit::TestResult::FAULT	, &self.method(:test_fault))
		mediator.add_listener(Test::Unit::TestCase::FINISHED , &self.method(:test_finished))
	end
	
	def test_started(test_name)
		Log.debug "starting test #{test_name}"

		@start_time	 = Time.now.to_i
		@test_status = 'P'
		@msg		 = ''
		@last_state  = ''
	 
		if @run_id != 'test'
			Database.query("update SuiteResults set state='R' where runId=#{@run_id} and suiteName='#{@suite_name}'")
			@last_state = UraniumQuery.get_case_state(@run_id, @suite_name, test_name)
			UraniumQuery.set_case_state(@run_id, @suite_name, test_name.sub(/\(.*/, ''), 'R')
		end
	end
	
	def test_finished(test_name)
		Log.debug "test #{test_name} complete\n"
		Log.debug "\n\n**Result: #{@test_status=='P' ? 'Pass' : 'Fail'}"
	
		duration		= Time.now.to_i - @start_time;
		name			= test_name.sub(/\(.*/, '')
										
		@test_status = 'E' if (@test_status == 'P' && @append && @last_state != 'P')
		
		if @run_id != 'test'
			UraniumQuery.set_case_state(@run_id, @suite_name, name, @test_status, @msg, @append)
			UraniumQuery.sync_suite_state(@suite_name, @run_id)
			UraniumQuery.increment_case_ran_count(@run_id, @suite_name, name)
		end
	end
	
	def test_fault(fault)
		@msg		 = Database.escape_string(fault.to_s) if @run_id != 'test'
		@test_status = 'F'
		
		Log.error fault.to_s
	end
	
	def suite_started(name='')
		UraniumQuery.set_suite_state(@run_id, @suite_name, 'R') if @run_id != 'test'
	end
	
	def suite_finished(name='')
	end
end

class TestRunner
	# Load all the testcases that we can find under path/*
	def self.load_test_scripts(path)
		Find.find(path) do |p|
			#only load files that end in _tests
			if p.match(/_tests\.rb$/i)
				begin
					Log.debug "requiring #{File.basename(p)}"
					require (File.expand_path(p))
				rescue Exception => e
					Log.debug "problem loading #{e.inspect}"
				end
			end
		end
	end
	
	def self.run_suite(suite_name, run_id, append=false)
		Log.debug "run suite #{suite_name} on run_id #{run_id}"
		begin
			test = nil
			
			begin
				eval("test = #{suite_name}.suite")
			rescue Exception => e
				Log.debug "suite #{suite_name} couldnt be found"
				UraniumQuery.omit_suite(suite_name, run_id)
				return
			end
						
			runner = TestObserver.new(suite_name, run_id, append)
			runner.run(test)
			UraniumQuery.sync_totals(run_id) if run_id != 'test'
		rescue Exception => e
			Log.debug e.message
			Log.debug e.backtrace.inspect
		end		
	end

	def self.run_test_case(test_name, suite_name, run_id)
		begin
			test = nil
			eval("test = #{suite_name}.new('#{test_name}')")
			UraniumQuery.set_suite_state(run_id, suite_name, 'R')
			
			eval("#{suite_name}.class.setup() if defined?(#{suite_name}.class.setup)")
			
			runner = TestObserver.new(suite_name, run_id, true)
			runner.run(test)
			
			eval("#{suite_name}.class.teardown() if defined?(#{suite_name}.class.teardown)")
		rescue Exception => e
			Log.debug "Line: #{suite_name}.new('#{test_name}') failed.	Class and or method doesnt exist"
			Log.debug e.message	
			Log.debug e.backtrace.inspect
		end
	end
end
